package com.example.demo.utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.DateUtil;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@SuppressWarnings("all")
public class CalendarUtils {
    /**
     * 每年的毫秒数
     */
    public static final long YEAR_MILLISECONDS = DateUtil.DAY_MILLISECONDS * 365;
    /**
     * 计算的最大时间跨度（以年为单位）
     */
    public static final long MAX_LEN_YEAR_LEN = 1;
    /**
     * 一周的天数间隔
     */
    public static final int WEEK_DAY_NUM = 7;

    // 日期格式化工具
    public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
            "yyyy-MM-dd");
    public static final SimpleDateFormat DATE_FORMAT_CN = new SimpleDateFormat(
            "yyyy年MM月dd日");
    // 日期时间格式化工具
    public static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat(
            "yyyy-MM-dd HH:mm:ss");

    public static final SimpleDateFormat DATETIME_FORMAT_HMS = new SimpleDateFormat(
            "yyyy年MM月dd日 HH:mm:ss");
    // 日期分割符
    public static final String DATE_SEPERATOR = "/";
    // 时间分割符
    public static final String TIME_SEPERATOR = ":";
    // 日期与时间之间的分隔符
    public static final String DATE_TIME_SEPERATOR = " ";
    // 包含日期与时间的分隔符
    public static final int CONTAINS_DATE_TIME_SEPERATOR = 0;
    // 只包含日期分隔符
    public static final int CONTAINS_DATE_SEPERATOR = 1;
    // 只包含时间分隔符
    public static final int CONTAINS_TIME_SEPERATOR = 2;
    // 不包含日期与时间分隔符
    public static final int CONTAINS_NONE_SEPERATOR = 3;
    public static final String CHINESE_NUM[] = {"一", "二", "三", "四", "五", "六",
            "七", "八", "九", "十", "十一", "十二"};
    public static final String CHINESE_MONTH[] = {"正月", "二月", "三月", "四月",
            "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"};
    public static final long[] LUNAR_INFO = new long[]{0x04bd8, 0x04ae0,
            0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0,
            0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,
            0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5,
            0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
            0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3,
            0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0,
            0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0,
            0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8,
            0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570,
            0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5,
            0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0,
            0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50,
            0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0,
            0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
            0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7,
            0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50,
            0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954,
            0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260,
            0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0,
            0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0,
            0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20,
            0x0ada0};
    public static final String DATE_HEAD_TIME_STR = "00:00:00";
    public static final String DATE_TAIL_TIME_STR = "23:59:59";

    /**
     * @return
     * @方法名称： getTimeSpaceMap
     * @功能说明：获取时间间隔map
     */
    public static List<String> getTimespaceList() {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        int date = c.get(Calendar.DATE);
        int currentDate = c.get(Calendar.DATE);
        List<String> list = new ArrayList<String>();
        do {
            DecimalFormat df = new DecimalFormat("00");
            String h = df.format(c.get(Calendar.HOUR_OF_DAY));
            String m = df.format(c.get(Calendar.MINUTE));
            String s = df.format(c.get(Calendar.SECOND));
            String key = h + ":" + m + ":" + s;
            list.add(key);
            c.add(Calendar.MINUTE, 30);
            date = c.get(Calendar.DATE);
        } while (date == currentDate);
        return list;
    }

    /**
     * @param date
     * @return
     * @方法名称： getWeekDay
     * @功能说明：获取日期的周时间描述
     */
    public static String getWeekDay(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int weekDay = c.get(Calendar.DAY_OF_WEEK);
        String day = "";
        switch (weekDay) {
            case Calendar.SUNDAY:
                day = "日";
                break;
            case Calendar.MONDAY:
                day = "一";
                break;
            case Calendar.TUESDAY:
                day = "二";
                break;
            case Calendar.WEDNESDAY:
                day = "三";
                break;
            case Calendar.THURSDAY:
                day = "四";
                break;
            case Calendar.FRIDAY:
                day = "五";
                break;
            case Calendar.SATURDAY:
                day = "六";
                break;
        }
        return day;
    }

    /**
     * @param date
     * @return
     * @方法名称： formatString
     * @功能说明：进行日期的字符串格式化
     */
    public static String formatString(Date date) {
        return formatString(date, DATE_FORMAT);
    }

    /**
     * @param date
     * @param df
     * @return
     * @方法名称： formatString
     * @功能说明：进行日期的字符串格式化
     */
    public static String formatString(Date date, SimpleDateFormat df) {
        return df.format(date);
    }

    /**
     * @param dateStr
     * @return
     * @方法名称： format
     * @功能说明：进行日期的格式化
     */
    public static Date format(String dateStr) {
        return format(dateStr, DATE_FORMAT);
    }

    /**
     * @param dateStr
     * @return
     * @方法名称： format
     * @功能说明：进行日期的格式化
     */
    public static Date format(String dateStr, SimpleDateFormat df) {
        try {
            if (StringUtils.isBlank(dateStr)) {
                return null;
            }
            return df.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param date
     * @return
     * @方法名称： getCnDate
     * @功能说明：获取农历日期
     */
    @SuppressWarnings("unused")
    public static String getCnDate(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int yearCyl, monCyl, dayCyl;
        int leapMonth = 0;
        int year, month, day;
        boolean leap;
        Date baseDate = null;
        try {
            baseDate = DATE_FORMAT.parse("1900-01-31");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 求出和1900年1月31日相差的天数
        int offset = (int) ((cal.getTime().getTime() - baseDate.getTime()) / 86400000L);
        dayCyl = offset + 40;
        monCyl = 14;
        // 用offset减去每农历年的天数
        // 计算当天是农历第几天
        // i最终结果是农历的年份
        // offset是当年的第几天
        int iYear, daysOfYear = 0;
        for (iYear = 1900; iYear < 2050 && offset > 0; iYear++) {
            daysOfYear = yearDays(iYear);
            offset -= daysOfYear;
            monCyl += 12;
        }
        if (offset < 0) {
            offset += daysOfYear;
            iYear--;
            monCyl -= 12;
        }
        // 农历年份
        year = iYear;
        yearCyl = iYear - 1864;
        leapMonth = leapMonth(iYear); // 闰哪个月,1-12
        leap = false;
        // 用当年的天数offset,逐个减去每月（农历）的天数，求出当天是本月的第几天
        int iMonth, daysOfMonth = 0;
        for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
            // 闰月
            if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
                --iMonth;
                leap = true;
                daysOfMonth = leapDays(year);
            } else {
                daysOfMonth = monthDays(year, iMonth);
            }
            offset -= daysOfMonth;
            // 解除闰月
            if (leap && iMonth == (leapMonth + 1)) {
                leap = false;
            }
            if (!leap) {
                monCyl++;
            }
        }
        // offset为0时，并且刚才计算的月份是闰月，要校正
        if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
            if (leap) {
                leap = false;
            } else {
                leap = true;
                --iMonth;
                --monCyl;
            }
        }
        // offset小于0时，也要校正
        if (offset < 0) {
            offset += daysOfMonth;
            --iMonth;
            --monCyl;
        }
        month = iMonth;
        day = offset + 1;
        String chineseTen[] = {"初", "十", "廿", "卅"};
        int n = day % 10 == 0 ? 9 : day % 10 - 1;
        if (day > 30) {
            return "";
        }
        if (day == 10) {
            return CHINESE_MONTH[month - 1] + "初十";
        } else {
            return CHINESE_MONTH[month - 1] + chineseTen[day / 10]
                    + CHINESE_NUM[n];
        }
    }

    /**
     * @param y
     * @return
     * @方法名称： yearDays
     * @功能说明：传回农历 y年的总天数
     */
    public static int yearDays(int y) {
        int i, sum = 348;
        for (i = 0x8000; i > 0x8; i >>= 1) {
            if ((LUNAR_INFO[y - 1900] & i) != 0)
                sum += 1;
        }
        return (sum + leapDays(y));
    }

    /**
     * @param y
     * @return
     * @方法名称： leapDays
     * @功能说明：传回农历 y年闰月的天数
     */
    public static int leapDays(int y) {
        if (leapMonth(y) != 0) {
            if ((LUNAR_INFO[y - 1900] & 0x10000) != 0)
                return 30;
            else
                return 29;
        } else
            return 0;
    }

    /**
     * @param y
     * @return
     * @方法名称： leapMonth
     * @功能说明：传回农历 y年闰哪个月 1-12 , 没闰传回 0
     */
    public static int leapMonth(int y) {
        return (int) (LUNAR_INFO[y - 1900] & 0xf);
    }

    /**
     * @param y
     * @param m
     * @return
     * @方法名称： monthDays
     * @功能说明：传回农历 y年m月的总天数
     */
    public static int monthDays(int y, int m) {
        if ((LUNAR_INFO[y - 1900] & (0x10000 >> m)) == 0)
            return 29;
        else
            return 30;
    }

    /**
     * @param val
     * @return
     * @方法名称： containsDateSeperator
     * @功能说明：判断是否包含日期分隔符
     */
    public static boolean containsDateSeperator(String val) {
        return val.indexOf(DATE_SEPERATOR) != -1;
    }

    /**
     * @param val
     * @return
     * @方法名称： containsTimeSeperator
     * @功能说明：判断是否包含时间分隔符
     */
    public static boolean containsTimeSeperator(String val) {
        return val.indexOf(TIME_SEPERATOR) != -1;
    }

    /**
     * @param val
     * @return
     * @方法名称： containsSeperator
     * @功能说明：判断字串包含的日期时间分隔符类型
     */
    public static int containsSeperator(String val) {
        if (containsDateSeperator(val) && containsTimeSeperator(val)) {
            return CONTAINS_DATE_TIME_SEPERATOR;
        }
        if (containsDateSeperator(val) && !containsTimeSeperator(val)) {
            return CONTAINS_DATE_SEPERATOR;
        }
        if (!containsDateSeperator(val) && containsTimeSeperator(val)) {
            return CONTAINS_TIME_SEPERATOR;
        }
        return CONTAINS_NONE_SEPERATOR;
    }

    /**
     * @param inputDate
     * @param type
     * @return
     * @方法名称： trunc
     * @功能说明：
     */
    public static Date trunc(Date inputDate, int type) {
        Calendar c = Calendar.getInstance();
        c.setTime(inputDate);
        Date date = null;
        switch (type) {
            case Calendar.SECOND:
                c.set(Calendar.MILLISECOND, 0);
                break;
            case Calendar.MINUTE:
                c.set(Calendar.MILLISECOND, 0);
                c.set(Calendar.SECOND, 0);
                break;
            case Calendar.HOUR:
                c.set(Calendar.MILLISECOND, 0);
                c.set(Calendar.SECOND, 0);
                c.set(Calendar.MINUTE, 0);
                break;
            case Calendar.DATE:
                c.set(Calendar.MILLISECOND, 0);
                c.set(Calendar.SECOND, 0);
                c.set(Calendar.MINUTE, 0);
                c.set(Calendar.HOUR, 0);
                break;
            case Calendar.MONTH:
                c.set(Calendar.MILLISECOND, 0);
                c.set(Calendar.SECOND, 0);
                c.set(Calendar.MINUTE, 0);
                c.set(Calendar.HOUR, 0);
                c.set(Calendar.DATE, 1);
                break;
            case Calendar.YEAR:
                c.set(Calendar.MILLISECOND, 0);
                c.set(Calendar.SECOND, 0);
                c.set(Calendar.MINUTE, 0);
                c.set(Calendar.HOUR, 0);
                c.set(Calendar.DATE, 1);
                c.set(Calendar.MONTH, 0);
                break;
            default:
                break;
        }
        date = c.getTime();
        return date;
    }

    /**
     * @param inputDate
     * @param daysNum
     * @param type
     * @return
     * @方法名称： add
     * @功能说明：
     */
    public static Date add(Date inputDate, Integer daysNum, int type) {
        Calendar c = Calendar.getInstance();
        c.setTime(inputDate);
        c.add(type, daysNum);
        return c.getTime();
    }

    /**
     * @param args
     * @方法名称： main
     * @功能说明：测试方法
     */
    public static void main(String[] args) {

        System.out.println(isDate("1",null));
        System.out.println(getTimespaceList());

        System.out.println(yearFigureOuter("2020-05-01 12:00:00", "2021-03-02 12:00:00"));
    }





    public static boolean isDate(String strDate, String type) {
        if (StringUtils.isBlank(strDate)) {
            return false;
        }
        boolean convertSuccess = true;
        SimpleDateFormat format = null;
        if (StringUtils.isNotBlank(type)) {
            format = new SimpleDateFormat("yyyy-MM-dd");
        }
        format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            format.setLenient(false);
            format.parse(strDate);
        } catch (ParseException e) {

            convertSuccess = false;

        }
        return convertSuccess;
    }


    /**
     * 计算相差周数
     */
    public static int weeksFigureOuter(String startDate, String endDate) {

        long startTime = CalendarUtils.format(startDate).getTime();
        long endTime = CalendarUtils.format(endDate).getTime();

        Long weekTime = (endTime - startTime) / (1000 * 3600 * 24 * 7);
        return weekTime.intValue();
    }

    /**
     * 计算相差月数
     */
    public static int monthFigureOuter(String startDate, String endDate) {

        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        start.setTime(CalendarUtils.format(startDate));
        end.setTime(CalendarUtils.format(endDate));
        int result = end.get(Calendar.MONTH) - start.get(Calendar.MONTH);
        int month = (end.get(Calendar.YEAR) - start.get(Calendar.YEAR)) * 12;

        return Math.abs(month + result);
    }

    /**
     * 计算相差年数
     */
    public static int yearFigureOuter(String startDate, String endDate) {


        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        start.setTime(CalendarUtils.format(startDate));
        end.setTime(CalendarUtils.format(endDate));

        return (end.get(Calendar.YEAR) - start.get(Calendar.YEAR));

    }

    /**
     * 计算相差天数
     */
    public static int getDays(Date sartDate, Date endDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(sartDate);
        long timeInMillis1 = calendar.getTimeInMillis();
        calendar.setTime(endDate);
        long timeInMillis2 = calendar.getTimeInMillis();

        long betweenDays = (timeInMillis2 - timeInMillis1) / (1000L * 3600L * 24L);

        return (int) betweenDays;
    }

    /**
     * 计算每个工作日重复
     */
    public static List<Date> workday(Date startDate) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        long timeLen = calendar.getTimeInMillis();
        long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;
        do {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay != Calendar.SUNDAY && weekDay != Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            calendar.add(Calendar.DATE, 1);
            timeLen = calendar.getTimeInMillis();
        } while (timeLen < endTimeLen);
        return dateList;
    }

    /**
     * 计算每个工作日重复
     */
    public static List<Date> workday(Date startDate, long date) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        long timeLen = calendar.getTimeInMillis();
        long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;
        do {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay != Calendar.SUNDAY && weekDay != Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            calendar.add(Calendar.DATE, 1);
            timeLen = calendar.getTimeInMillis();
        } while (timeLen < date);
        return dateList;
    }

    /**
     * 计算每个工作日重复
     */
    public static List<Date> workday(Date startDate, int date) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        long timeLen = calendar.getTimeInMillis();
        long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;
        date =+ date / 5 * 2 + date;
        for (int i = 1; i <= date; i++) {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay != Calendar.SUNDAY && weekDay != Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            calendar.add(Calendar.DATE, 1);
            timeLen = calendar.getTimeInMillis();
        }
        return dateList;
    }


    /**
     * 计算每日休息日
     */
    public static List<Date> dayOff(Date startDate) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        long timeLen = calendar.getTimeInMillis();
        long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;
        do {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay == Calendar.SUNDAY || weekDay == Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            calendar.add(Calendar.DATE, 1);
            timeLen = calendar.getTimeInMillis();
        } while (timeLen < endTimeLen);
        return dateList;
    }

    /**
     * 计算每日休息日
     */
    public static List<Date> dayOff(Date startDate, long date) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        long timeLen = calendar.getTimeInMillis();
        long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;
        do {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay == Calendar.SUNDAY || weekDay == Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            calendar.add(Calendar.DATE, 1);
            timeLen = calendar.getTimeInMillis();
        } while (timeLen < date);
        return dateList;
    }

    /**
     * 计算每周休息日
     */
    public static List<Date> dayOff(Date startDate, int date) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();
        //long timeLen = calendar.getTimeInMillis();
        /*long endTimeLen = timeLen + CalendarUtils.MAX_LEN_YEAR_LEN
                * CalendarUtils.YEAR_MILLISECONDS;*/
        while (dateList.size() < date) {
            int weekDay = calendar.get(Calendar.DAY_OF_WEEK);
            if (weekDay == Calendar.SUNDAY || weekDay == Calendar.SATURDAY) {
                dateList.add(calendar.getTime());
            }
            if (calendar.get(Calendar.DAY_OF_WEEK) == 7) {
                calendar.add(Calendar.DATE, 1);
            } else {
                calendar.add(Calendar.DATE, 7 - weekDay);
            }
            //timeLen = calendar.getTimeInMillis();
        }
        return dateList;
    }

    /**
     * 计算往后每个月当前日
     */
    public static List<Date> monthToDay(Date startDate) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();

        for (int i = 1; i<= 36; i++){
            calendar.add(Calendar.MONTH, 1);
        }
        return dateList;
    }

    /**
     * 计算往后每个月当前日
     */
    public static List<Date> yearToDay(Date startDate) throws Exception {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(startDate);
        List<Date> dateList = new ArrayList<Date>();

        for (int i = 1; i<= 36; i++){
            calendar.add(Calendar.YEAR, 1);
        }
        return dateList;
    }
}
